﻿using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Xml.Linq;

namespace System.Data
{
    /// <summary>
    /// Provides DataRowCollection functionality
    /// </summary>
    public class DataRowCollection : Collection<DataRow>
    {
        #region Data Fields
        /// <summary>
        /// True if owner is DataTable or False if DataView
        /// </summary>
        private bool _ownerIsDataTable;

        /// <summary>
        /// "SelectedRows" property data field
        /// </summary>
        private DataRowCollection _selectedRows;
        #endregion

        #region Events
        /// <summary>
        /// Another row was selected
        /// </summary>
        internal event EventHandler<DataSetEventArgs> AnotherRowWasSelected;

        /// <summary>
        /// INotifyPropertyChanged interface implementation
        /// </summary>
        internal event PropertyChangedEventHandler PropertyChanged;
        #endregion

        #region Constructors
        /// <summary>
        /// Default constructor
        /// <param name="ownerIsDataTable">Pass True if owner is DataTable or False if DataView</param>
        /// </summary>
        internal DataRowCollection(bool ownerIsDataTable)
        {
            this._ownerIsDataTable = ownerIsDataTable;
        }
        #endregion

        #region Properties
        /// <summary>
        /// Gets selected rows
        /// </summary>
        internal DataRowCollection SelectedRows
        {
            get
            {
                return this._selectedRows;
            }
        }
        #endregion

        #region Methods
        /// <summary>
        /// Adds row to collection
        /// </summary>
        /// <param name="dataRow">Row for adding</param>
        /// <returns>Added row</returns>
        public new DataRow Add(DataRow dataRow)
        {
            // Validate argument
            if (dataRow == null)
            {
                throw new ArgumentNullException("dataRow");
            }

            // Vallidate required fields
            for (int i = 0; i < dataRow.Table.Columns.Count; i++)
            {
                if (!dataRow.Table.Columns[i].AllowDBNull && dataRow.ItemArray[i].Value == null)
                {
                    throw new InvalidOperationException("Value cannot be null inside required \"" + dataRow.Table.Columns[i].ColumnName + "\" field");
                }
            }

            dataRow.Index = base.Count;

            // Only DataTable can change DataRow state and version
            if (this._ownerIsDataTable)
            {
                dataRow.EndEdit();
                dataRow.State = DataRowState.Added;
                dataRow.RowVersion = DataRowVersion.Current;
            }

            dataRow.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(DataRow_PropertyChanged);
            dataRow.RowDeleted += new EventHandler<EventArgs>(DataRow_RowDeleted);
            dataRow.RowSelected += new EventHandler<EventArgs>(DataRow_RowSelected);
            base.Add(dataRow);
            if (dataRow.HasErrors)
            {
                this.DataRow_PropertyChanged(dataRow, new PropertyChangedEventArgs("HasErrors"));
            }

            return dataRow;
        }


        //public new DataRow Add(object[] arr)
        //{
        //    // Validate argument
        //    if (arr == null)
        //    {
        //        throw new ArgumentNullException("dataRow");
        //    }

        //    // Vallidate required fields
        //    for (int i = 0; i < dataRow.Table.Columns.Count; i++)
        //    {
        //        if (!dataRow.Table.Columns[i].AllowDBNull && arr.GetValue(i) == null)
        //        {
        //            throw new InvalidOperationException("Value cannot be null inside required \"" + dataRow.Table.Columns[i].ColumnName + "\" field");
        //        }
        //    }


        //    dataRow.Index = base.Count;

        //    // Only DataTable can change DataRow state and version
        //    if (this._ownerIsDataTable)
        //    {
        //        dataRow.EndEdit();
        //        dataRow.State = DataRowState.Added;
        //        dataRow.RowVersion = DataRowVersion.Current;
        //    }

        //    dataRow.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(DataRow_PropertyChanged);
        //    dataRow.RowDeleted += new EventHandler<EventArgs>(DataRow_RowDeleted);
        //    dataRow.RowSelected += new EventHandler<EventArgs>(DataRow_RowSelected);
        //    base.Add(dataRow);
        //    if (dataRow.HasErrors)
        //    {
        //        this.DataRow_PropertyChanged(dataRow, new PropertyChangedEventArgs("HasErrors"));
        //    }

        //    return dataRow;
        //}




        /// <summary>
        /// Clears DataRowCollection
        /// </summary>
        public new void Clear()
        {
            // Unsubscribe from all events
            for (int i = 0; i < base.Count; i++)
            {
                DataRow dataRow = base[i];
                this.PrepareForRemoving(dataRow);
            }

            // Clear DataCollection
            base.Clear();
        }

        /// <summary>
        /// Prepares DataRow for removing from DatRowCollection
        /// </summary>
        /// <param name="dataRow">DataRow instance</param>
        private void PrepareForRemoving(DataRow dataRow)
        {
            dataRow.RowDeleted -= DataRow_RowDeleted;
            dataRow.RowSelected -= DataRow_RowSelected;
            dataRow.PropertyChanged -= DataRow_PropertyChanged;
        }

        /// <summary>
        /// Removes DataRow from DataRowCollection
        /// </summary>
        /// <param name="dataRow">DataRow instance</param>
        public new void Remove(DataRow dataRow)
        {
            this.PrepareForRemoving(dataRow);
            base.Remove(dataRow);
        }
        #endregion

        #region Event Handlers
        /// <summary>
        /// Handles DataRow.RowDeleted event
        /// </summary>
        /// <param name="sender">Event source</param>
        /// <param name="e">Event arguments</param>
        void DataRow_RowDeleted(object sender, EventArgs e)
        {
            Remove((DataRow)sender);
        }

        /// <summary>
        /// Handles DataField.PropertyChanged event
        /// </summary>
        /// <param name="sender">Event source</param>
        /// <param name="e">Event arguments</param>
        void DataRow_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (PropertyChanged != null)
            {
                if (e.PropertyName.Contains("HasError"))
                {
                    PropertyChanged(this, e);
                }
            }
        }

        /// <summary>
        /// Handles DataRow.RowSelected event
        /// </summary>
        /// <param name="sender">Event source</param>
        /// <param name="e">Event arguments</param>
        void DataRow_RowSelected(object sender, EventArgs e)
        {
            // Create collection of selected rows if it doesn't exist
            if (this._selectedRows == null)
            {
                this._selectedRows = new DataRowCollection(false);
            }

            // Add or remove DataRow  depending on it selection
            DataRow dataRow = (DataRow)sender;
            if (dataRow.Selected)
            {
                this._selectedRows.Add(dataRow);
            }
            else
            {
                this._selectedRows.Remove(dataRow);
            }

            // Raise AnotherRowWasSelected event if at least one DataRow is selected
            if (this._selectedRows.Count > 0)
            {
                if (AnotherRowWasSelected != null)
                {
                    DataSetEventArgs dataSetEventArgs = new DataSetEventArgs();
                    dataSetEventArgs.DataTableName = this._selectedRows[0].Table.TableName;
                    AnotherRowWasSelected(this._selectedRows, dataSetEventArgs);
                }
            }
        }
        #endregion
    }
}
